home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Technical Documentation / Sample Code / DTS.Lib & Samples / Kibitz / Chess.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-22  |  51.8 KB  |  1,914 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        chess.c
  5. ** Written by:  Eric Soldan
  6. **
  7. ** Copyright © 1990-1992 Apple Computer, Inc.
  8. ** All rights reserved. */
  9.  
  10.  
  11.  
  12. /*****************************************************************************/
  13.  
  14.  
  15.  
  16. #include "Kibitz.h"                /* Get the Kibitz includes/typedefs, etc.    */
  17. #include "KibitzCommon.h"        /* Get the stuff in common with rez.        */
  18. #include "Kibitz.protos"        /* Get the prototypes for Kibitz.            */
  19.  
  20. #ifndef __ERRORS__
  21. #include <Errors.h>
  22. #endif
  23.  
  24. #ifndef __FONTS__
  25. #include <Fonts.h>
  26. #endif
  27.  
  28. #ifndef __TEXTEDITCONTROL__
  29. #include <TextEditControl.h>
  30. #endif
  31.  
  32. #ifndef __TOOLUTILS__
  33. #include <ToolUtils.h>
  34. #endif
  35.  
  36. #ifndef __STDIO__
  37. #include <StdIO.h>
  38. #endif
  39.  
  40. #ifndef __STRING__
  41. #include <String.h>
  42. #endif
  43.  
  44. #ifndef THINK_C
  45. #ifndef __STRINGS__
  46. #include <Strings.h>
  47. #endif
  48. #endif
  49.  
  50. #ifndef __UTILITIES__
  51. #include <Utilities.h>
  52. #endif
  53.  
  54.  
  55.  
  56. /*****************************************************************************/
  57.  
  58.  
  59.  
  60. extern Boolean    gComputerResigns;
  61.  
  62. short    gPieceLoc;
  63.  
  64. #define kLastNode         6
  65. #define kComputerResigns -9999
  66.  
  67. static GameListHndl        gGenMovesHndl;
  68. static unsigned long    idleTick;
  69. static MoveListHndl        gNodeHndl[kLastNode + 1];
  70.  
  71. static short    gNumPieces, gPosReps;
  72. static long    gTreeValue, gWhiteTotal, gBlackTotal;
  73. static long    gTreePieceValues[13] = {
  74.     -0x40000000L, -0x00090000L, -0x00050000L, -0x00030000L, -0x00030000L, -0x00010000L,
  75.      0x00000000L,
  76.      0x00010000L,  0x00030000L,  0x00030000L,  0x00050000L,  0x00090000L,  0x40000000L,
  77. };
  78.  
  79. static short    distance[10] = {0, 1, 1, 7, 7, 7, 1};
  80.     /* How far a piece can move.                        */
  81.     /* The double-pawn-push is handled as an exception. */
  82.     /* Castling is handled as an exception.                */
  83.  
  84. static short    direction[10][9] = {
  85.       0,   0,   0,   0,   0,   0,   0,   0,   0,
  86.      10,   9,  11,   0,   0,   0,   0,   0,   0,    /* Pawn moves.     */
  87.     -21, -19, -12,  -8,   8,  12,  19,  21,   0,    /* Knight moves. */
  88.     -11,  -9,   9,  11,   0,   0,   0,   0,   0,    /* Bishop moves. */
  89.     -10,  -1,   1,  10,   0,   0,   0,   0,   0,    /* Rook moves.     */
  90.     -11,  -9,   9,  11, -10,  -1,   1,  10,   0,    /* Queen moves.     */
  91.     -11,  -9,   9,  11, -10,  -1,   1,  10,   0,    /* King moves.     */
  92. };
  93.  
  94. static short    gPieceColor[13] = {WHITE, WHITE, WHITE, WHITE, WHITE, WHITE,
  95.                                    EMPTY,
  96.                                    BLACK, BLACK, BLACK, BLACK, BLACK, BLACK};
  97.  
  98. static short    gPieceKind[13]  = {KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN,
  99.                                    EMPTY,
  100.                                    PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING};
  101.  
  102.  
  103.  
  104. /*****************************************************************************/
  105.  
  106.  
  107.  
  108. TheDoc    newDocData = {
  109.     kVersion,        /* File format version.                             */
  110.  
  111.     false,            /* Flag indicating print record is current.         */
  112.     {                /* Space for print record.                         */
  113.         0,
  114.         {0, 0, 0,{0, 0, 0, 0},},
  115.         {0, 0, 0, 0},
  116.         {0, 0, 0, 0, 0},
  117.         {0, 0, 0,{0, 0, 0, 0},},
  118.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  119.         {0, 0, 0, 0, 0, nil, nil, 0, 0, 0},
  120.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  121.     },
  122.  
  123.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  124.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  125.     OBNDS,  BR,    BN,    BB,    BQ,    BK,    BB,    BN,    BR,   OBNDS,
  126.     OBNDS,  BP,    BP,    BP,    BP,    BP,    BP,    BP,    BP,   OBNDS,
  127.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  128.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  129.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  130.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  131.     OBNDS,  WP,    WP,    WP,    WP,    WP,    WP,    WP,    WP,   OBNDS,
  132.     OBNDS,  WR,    WN,    WB,    WQ,    WK,    WB,    WN,    WR,   OBNDS,
  133.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  134.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  135.     {
  136.         WKPOS, 0,    /* White king position, king-moved count.         */
  137.         0,            /* White queen-rook-moved count.                 */
  138.         0,            /* White king-rook-moved count.                     */
  139.         BKPOS, 0,    /* Black king position, king-moved count.         */
  140.         0,            /* Black queen-rook-moved count.                 */
  141.         0            /* Black king-rook-moved count.                     */
  142.     },
  143.     0,                /* En-passant opportunity location.                 */
  144.     0,                /* En-passant opportunity pawn-to-take location. */
  145.     0,                /* Arranged en-passant opportunity loc.             */
  146.     0,                /* Arranged en-passant opp. pawn-to-take loc.     */
  147.     0,                /* Number of legal moves in move list.             */
  148.     0,                /* Index into record of game.                     */
  149.     0,                /* Number of moves in game.                         */
  150.     0,                /* My color (0 = white).                         */
  151.     0,                /* True if black started game.                     */
  152.     0,                /* True if in arrange-board mode.                 */
  153.     WP,                /* Piece hilited in arrange-board palette.         */
  154.     18000L,            /* 5 minute default time for white.                 */
  155.     18000L,            /* 5 minute default time for black.                 */
  156.     -1L,            /* Ticks remaining for white. (-1, no clock)     */
  157.     -1L,            /* Ticks remaining for black. (-1, no clock)     */
  158.     false,            /* Display board normal (not inverted.)             */
  159.     0,                /* Above info is saved to disk.                     */
  160.  
  161.     "\0",            /* Space for opponent zone.                         */
  162.     "\0",            /* Space for opponent machine.                     */
  163.     "\0",            /* Space for opponent kibitz full path.             */
  164.     "\0",            /* Space for opponent kibitz filename.             */
  165.     false,            /* Boolean for just board window.                 */
  166.     false,            /* Boolean for document is template.             */
  167.     false,            /* Document saved while computer moves white.     */
  168.     false,            /* Document saved while computer moves black.     */
  169.     0,                /* Above is new version info saved to disk.         */
  170.  
  171.     false,            /* Flag indicating existence of opponent.         */
  172.     0L,                /* ID assigned by me for this game.                 */
  173.     0L,                /* ID assigned by opponent for this game.         */
  174.     0,                /* Reason for sending the game.                     */
  175.     0,                /* State of the draw button.                      */
  176.     {
  177.         0L,            /* AEAddressDesc of opponent.                     */
  178.         nil
  179.     },
  180.     0,                /* Above is send game info.                         */
  181.  
  182.     0,                /* State for receiving AppleEvents.                 */
  183.     false,            /* Flag indicating if we originated game.         */
  184.     -1L,            /* Ticks remaining displayed for white.             */
  185.     -1L,            /* Ticks remaining displayed for black.             */
  186.     -1L,            /* Ticks for freeze clock for white.             */
  187.     -1L,            /* Ticks for freeze clock for black.             */
  188.     0L,                /* Reference tick for timer.                     */
  189.     0L,                /* Tick when computer moved last.                 */
  190.     0L,                /* Tick when received last info from opponent.     */
  191.     false,            /* Flag indicating computer moves white pieces.     */
  192.     false,            /* Flag indicating computer moves black pieces.     */
  193.     "\0",            /* Space for opponent name.                         */
  194.     "\0",            /* Space for opponent zone.                         */
  195.     "\0",            /* Space for opponent machine.                     */
  196.     0,                /* Time that last move/message received.         */
  197.     0,                /* Above info is for one machine only.             */
  198.  
  199.     false,            /* Flag indicating color change has been posted. */
  200.     0,                /* New color from config.                         */
  201.     false,            /* Flag indicating time change has been posted.     */
  202.     -1L,            /* New white time from config.                     */
  203.     -1L,            /* New white time from config.                     */
  204.     0,                /* Above info is config setting which will         */
  205.                     /* not be applied until a NULL event.             */
  206.  
  207.     nil,            /* Handle to legal move list.                     */
  208.     nil,            /* Handle to game record.                         */
  209.     nil,            /* Handle to incoming message.                     */
  210.     nil,            /* Handle to outgoing message.                     */
  211.     nil,            /* Handle to recorded sound, if any.             */
  212.     nil,            /* Handle to send button control.                 */
  213.     nil,            /* Handle to move notify control.                 */
  214.     nil,            /* Handle to message notify control.             */
  215.     nil,            /* Handle to game-slider control.                 */
  216.     nil,            /* Handle to white-starts radio button.             */
  217.     nil,            /* Handle to black-starts radio button.             */
  218.     nil,            /* Handle to resign button.                         */
  219.     nil,            /* Handle to draw button.                         */
  220.     nil,            /* Handle to record sound button.                 */
  221.     nil,            /* Handle to send sound button.                     */
  222.     0,                /* Above info is reference to controls.             */
  223. };
  224.  
  225.  
  226.  
  227. /*****************************************************************************/
  228.  
  229.  
  230.  
  231. #pragma segment Chess
  232. OSErr    InitLogic(void)
  233. {
  234.     short    i;
  235.  
  236.     if (!(gGenMovesHndl = (GameListHndl)NewHandle(0))) return(memFullErr);
  237.  
  238.     for (i = 0; i <= kLastNode; ++i)
  239.         if (!(gNodeHndl[i] = (MoveListHndl)NewHandle(0))) return(memFullErr);
  240.  
  241.     return(noErr);
  242. }
  243.  
  244.  
  245.  
  246. /*****************************************************************************/
  247.  
  248.  
  249.  
  250. #pragma segment Chess
  251. void    NewGame(FileRecHndl game)
  252. {
  253.     TheDocPtr    docPtr;
  254.  
  255.     docPtr = &(*game)->doc;
  256.  
  257.     newDocData.legalMoves = docPtr->legalMoves;
  258.     newDocData.gameMoves  = docPtr->gameMoves;
  259.  
  260.     *docPtr = newDocData;
  261.  
  262.     newDocData.legalMoves = nil;
  263.     newDocData.gameMoves  = nil;
  264. }
  265.  
  266.  
  267.  
  268. /*****************************************************************************/
  269.  
  270.  
  271.  
  272. #pragma segment Chess
  273. void    GenerateLegalMoves(FileRecHndl game)
  274. {
  275.     short            gameIndex, numGameMoves, square, piece;
  276.     short            color, pieceColor;
  277.     short            row, dirNum, dir, dist;
  278.     short            s, d, dest, destColor, epLoc;
  279.     long            size;
  280.     Boolean            docDirty, check;
  281.     GameListHndl    gameMoves;
  282.  
  283.     gameIndex    = (*game)->doc.gameIndex;
  284.     gameMoves    = (*game)->doc.gameMoves;
  285.     numGameMoves = (*game)->doc.numGameMoves;
  286.     docDirty     = (*game)->fileState.docDirty;
  287.  
  288.     (*game)->doc.numLegalMoves = 0;            /* Start the list over. */
  289.  
  290.     if ((gameIndex) && (gameIndex == numGameMoves))
  291.         if (!(**gameMoves)[gameIndex - 1].moveFrom) return;
  292.             /* Resignation or agreed-upon draw recorded.
  293.             ** Game over, so no legal moves. */
  294.  
  295.     SetHandleSize((Handle)gGenMovesHndl, size = GetHandleSize((Handle)gameMoves));
  296.     BlockMove(*(Handle)gameMoves, *(Handle)gGenMovesHndl, size);
  297.     (*game)->doc.gameMoves = gGenMovesHndl;            /* Protect the game moves list. */
  298.  
  299.     color = WhosMove(game);                        /* Who's move it is. */
  300.  
  301.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  302.         /* Scan for pieces of correct color. */
  303.  
  304.         if ((piece = (*game)->doc.theBoard[square]) == EMPTY) continue;
  305.             /* Empty square, so next square, please. */
  306.  
  307.         if (piece == OBNDS) continue;
  308.             /* Out of bounds. */
  309.  
  310.         pieceColor = BLACK;
  311.         if (piece < 0) {
  312.             pieceColor = WHITE;
  313.             piece = -piece;
  314.         }
  315.  
  316.         if (pieceColor != color) continue;
  317.             /* Not our piece. */
  318.  
  319.         row = square / 10;
  320.  
  321.         for (dirNum = 0; dir = direction[piece][dirNum]; ++dirNum) {
  322.             /* The direction we will move a piece.  This is correct in all
  323.             ** cases except for white pawns.  Without an adjustment, they
  324.             ** would move in the direction of black pawns, i.e., backwards.
  325.             */
  326.  
  327.             dist = distance[piece];
  328.                 /* The distance a piece can move, in all cases except a
  329.                 ** double-pawn-push and castling.
  330.                 */
  331.  
  332.             if (piece == PAWN) {
  333.                 if (color == WHITE) dir = -dir;
  334.                     /* White pawns will now move forwards. */
  335.  
  336.                 if ((!dirNum) && ((row == 3) || (row == 8))) dist = 2;
  337.                     /* In the case of a pawn, the first direction we check
  338.                     ** is forwards, so if dirNum is 0, we are pushing pawns.
  339.                     **
  340.                     ** Allow double-pawn-push if pawn is on correct row.  We don't
  341.                     ** have to worry about which color the pawn is if the pawn
  342.                     ** has advanced to the other double-push row.  It will
  343.                     ** double-push itself out of bounds.
  344.                     */
  345.             }
  346.  
  347.             for (s = square, d = 1; d <= dist; ++d) {
  348.  
  349.                 s += dir;
  350.                 dest = (*game)->doc.theBoard[s];
  351.                 if (dest == OBNDS) break;    /* Can't go this direction anymore. */
  352.  
  353.                 destColor = BLACK;
  354.                 if (dest < 0) {
  355.                     destColor = WHITE;
  356.                     dest = -dest;
  357.                 }
  358.  
  359.                 if ((dest) && (destColor == color)) break;
  360.                     /* Ran into our own piece, so can't go this direction anymore. */
  361.  
  362.                 if (dest == KING) break;
  363.                     /* Never allow the king to be taken. */
  364.  
  365.                 if (piece == PAWN) {
  366.                     if (!dirNum) {                /* If pawn push... */
  367.                         if (dest) break;        /* Can't take on a pawn-push. */
  368.                     }
  369.                     else {
  370.                         if (dest == EMPTY) {    /* If possible en-passant... */
  371.                             if ((*game)->doc.enPasMove != s) break;
  372.                                 /* Not en-passant pawn capture. */
  373.                             epLoc = (*game)->doc.enPasPawnLoc;
  374.                             dest  = (*game)->doc.theBoard[epLoc];
  375.                             destColor = BLACK;
  376.                             if (dest < 0) {
  377.                                 destColor = WHITE;
  378.                                 dest = -dest;
  379.                             }
  380.                             if (destColor == color) break;
  381.                                 /* We can't en-passant our own piece. */
  382.                             if (dest != PAWN) break;
  383.                                 /* We can only en-passant pawns. */
  384.                         }
  385.                     }
  386.                 }
  387.  
  388.                 MakeMove(game, square, s, QUEEN);
  389.                 check = SquareAttacked(game,
  390.                     (*game)->doc.king[color].kingLoc, color);
  391.                 UnmakeMove(game);
  392.                 if (!check) AddLegalMove(game, square, s);
  393.                     /* Move didn't put (or leave) king in check, so it is a
  394.                     ** valid move.  Since it is valid, record it. */
  395.  
  396.                 if (dest) break;    /* Once we hit a piece, we are
  397.                                     ** done in this direction. */
  398.             }
  399.         }
  400.     }
  401.  
  402.     square = (*game)->doc.king[color].kingLoc;
  403.     if (CastleOkay(game, QSIDE)) AddLegalMove(game, square, square - 2);
  404.     if (CastleOkay(game, KSIDE)) AddLegalMove(game, square, square + 2);
  405.         /* If castling possible, add it to move list. */
  406.  
  407.     (*game)->doc.gameMoves      = gameMoves;
  408.     (*game)->doc.numGameMoves   = numGameMoves;
  409.     (*game)->fileState.docDirty = docDirty;
  410.         /* Restore things the way we were.  We are done with MakeMove services. */
  411. }
  412.  
  413.  
  414.  
  415. /*****************************************************************************/
  416.  
  417.  
  418.  
  419. #pragma segment Chess
  420. void    AddLegalMove(FileRecHndl game, short from, short to)
  421. {
  422.     MoveListHndl    lglMoves;
  423.     short            numLglMoves;
  424.     long            newHndlSize, oldHndlSize;
  425.  
  426.     numLglMoves = (*game)->doc.numLegalMoves;
  427.     lglMoves    = (*game)->doc.legalMoves;
  428.  
  429.     oldHndlSize = GetHandleSize((Handle)lglMoves);
  430.     newHndlSize = ((numLglMoves | 0x3F) + 1) * sizeof(MoveElement);
  431.     if (newHndlSize != oldHndlSize)
  432.         SetHandleSize((Handle)lglMoves, newHndlSize);
  433.  
  434.     (**lglMoves)[numLglMoves].moveFrom = from;
  435.     (**lglMoves)[numLglMoves].moveTo   = to;
  436.     (**lglMoves)[numLglMoves].value    = 0;
  437.  
  438.     ++(*game)->doc.numLegalMoves;
  439. }
  440.  
  441.  
  442.  
  443. /*****************************************************************************/
  444.  
  445.  
  446.  
  447. #pragma segment Chess
  448. Boolean    CastleOkay(FileRecHndl game, short castleSide)
  449. {
  450.     short    color;
  451.     short    castleDir, kingLoc, rookLoc;
  452.     short    i, j, piece, pieceColor;
  453.  
  454.     color = WhosMove(game);            /* Who's move it is. */
  455.  
  456.     if ((*game)->doc.king[color].kingMoves) return(false);
  457.         /* Can't castle.  King has already moved. */
  458.  
  459.     rookLoc = (kingLoc = (*game)->doc.king[color].kingLoc) + 3;
  460.     if ((castleDir = castleSide) == QSIDE) {
  461.         rookLoc -= 7;
  462.         --castleDir;
  463.     }
  464.  
  465.     for (i = kingLoc, j = 3; j; i += castleDir, --j)
  466.         if (SquareAttacked(game, i, color)) return(false);
  467.             /* Can't castle out of, through, or into check. */
  468.  
  469.     if ((*game)->doc.king[color].rookMoves[castleSide]) return(false);
  470.         /* Rook trying to castle with has already moved,
  471.         ** or has been taken.
  472.         */
  473.  
  474.     piece = (*game)->doc.theBoard[rookLoc];
  475.     pieceColor = BLACK;
  476.     if (piece < 0) {
  477.         pieceColor = WHITE;
  478.         piece = -piece;
  479.     }
  480.     if (color != pieceColor) return(false);
  481.     if (piece != ROOK)         return(false);
  482.         /* These deviant conditions can occur if user arranged the board. */
  483.  
  484.     /* So far, everything is cool.  The only remaining possible problem is
  485.     ** that there is a piece (or more) between the king and the rook. */
  486.  
  487.     while (kingLoc += castleDir, kingLoc != rookLoc)
  488.         if ((*game)->doc.theBoard[kingLoc]) return(false);
  489.             /* There is a piece in the way, so we can't castle. */
  490.  
  491.     return(true);        /* The castling move is okay. */
  492. }
  493.  
  494.  
  495.  
  496. /*****************************************************************************/
  497.  
  498.  
  499.  
  500. #pragma segment Chess
  501. void    MakeMove(FileRecHndl game, short moveFrom, short moveTo, short promoteTo)
  502. {
  503.     GameListHndl    gameMoves;
  504.     long            newHndlSize, oldHndlSize;
  505.     short            gameIndex, numGameMoves, color, rank, i;
  506.     short            pieceMoved, pieceCaptured, pieceCapturedFrom;
  507.     short            absPieceMoved, delta, middle, oldRookLoc;
  508.     Boolean            modifyGame;
  509.  
  510.     if (moveFrom == -1) {
  511.         UnmakeMove(game);
  512.         return;
  513.     }
  514.  
  515.     gameIndex    = (*game)->doc.gameIndex;
  516.     numGameMoves = (*game)->doc.numGameMoves;
  517.     gameMoves    = (*game)->doc.gameMoves;
  518.     color        = WhosMove(game);
  519.  
  520.     i = (gameIndex > numGameMoves) ? gameIndex : numGameMoves;
  521.     newHndlSize = ((i | 0x3F) + 1) * sizeof(GameElement);
  522.     oldHndlSize = GetHandleSize((Handle)gameMoves);
  523.     if (newHndlSize != oldHndlSize)
  524.         SetHandleSize((Handle)gameMoves, newHndlSize);
  525.  
  526.     modifyGame = true;
  527.  
  528.     if (moveFrom == 1) {
  529.         if (gameIndex >= numGameMoves) return;
  530.             /* Already positioned at the end of the game. */
  531.         moveFrom   = (**gameMoves)[gameIndex].moveFrom;
  532.         moveTo     = (**gameMoves)[gameIndex].moveTo;
  533.         promoteTo  = (**gameMoves)[gameIndex].promoteTo;
  534.         modifyGame = false;
  535.     }
  536.  
  537.     if (!moveFrom) {        /* Draw agreed upon, or player resigned. */
  538.         if (gameIndex)
  539.             if (!(**gameMoves)[gameIndex - 1].moveFrom) --gameIndex;
  540.                 /* In case there is a race condition, only allow one game-ending
  541.                 ** "move" to occur. */
  542.         (**gameMoves)[gameIndex].moveFrom          = 0;
  543.         (**gameMoves)[gameIndex].moveTo            = moveTo;
  544.         (**gameMoves)[gameIndex].pieceCaptured     = 0;
  545.         (**gameMoves)[gameIndex].pieceCapturedFrom = 0;
  546.         (**gameMoves)[gameIndex].promoteTo         = 0;
  547.         (*game)->doc.gameIndex = ++gameIndex;
  548.         (*game)->doc.numGameMoves   = gameIndex;
  549.         (*game)->fileState.docDirty = true;
  550.         (*game)->doc.resync = kResync;
  551.         return;
  552.     }
  553.  
  554.     pieceMoved        = (*game)->doc.theBoard[moveFrom];
  555.     pieceCaptured     = (*game)->doc.theBoard[moveTo];
  556.     pieceCapturedFrom = moveTo;
  557.  
  558.     absPieceMoved = (pieceMoved < 0) ? -pieceMoved : pieceMoved;
  559.  
  560.     if (absPieceMoved == PAWN) {
  561.  
  562.         rank = moveTo / 10;
  563.         if ((rank == 2) || (rank == 9)) {
  564.             if (promoteTo < 0) promoteTo = -promoteTo;
  565.             pieceMoved *= promoteTo;
  566.             promoteTo = pieceMoved;
  567.         }
  568.         else promoteTo = 0;
  569.  
  570.         if (moveTo == (*game)->doc.enPasMove) {
  571.             pieceCaptured     = -pieceMoved;
  572.             pieceCapturedFrom = (*game)->doc.enPasPawnLoc;
  573.         }        /* If pawn move is onto en-passant move square, then the
  574.                 ** capture is from a square other than moveTo.
  575.                 */
  576.     }
  577.     else promoteTo = 0;
  578.  
  579.     (**gameMoves)[gameIndex].moveFrom          = moveFrom;
  580.     (**gameMoves)[gameIndex].moveTo            = moveTo;
  581.     (**gameMoves)[gameIndex].pieceCaptured     = pieceCaptured;
  582.     (**gameMoves)[gameIndex].pieceCapturedFrom = pieceCapturedFrom;
  583.     (**gameMoves)[gameIndex].promoteTo         = promoteTo;
  584.     (*game)->doc.gameIndex = ++gameIndex;
  585.  
  586.     if (modifyGame) {
  587.         (*game)->doc.numGameMoves   = gameIndex;
  588.         (*game)->fileState.docDirty = true;
  589.     }
  590.  
  591.     /* The move has now been recorded in the move list.  Now make the move. */
  592.  
  593.     (*game)->doc.theBoard[pieceCapturedFrom] = 0;
  594.     (*game)->doc.theBoard[moveTo]            = pieceMoved;
  595.     (*game)->doc.theBoard[moveFrom]          = 0;
  596.         /* The move is now made, except for the rook if castling. */
  597.  
  598.     delta  = moveTo - moveFrom;
  599.     middle = (moveTo + moveFrom) / 2;
  600.  
  601.     if (absPieceMoved == KING) {
  602.         oldRookLoc = 0;
  603.         if (delta == -2) oldRookLoc = moveFrom - 4;
  604.         if (delta == 2)  oldRookLoc = moveFrom + 3;
  605.         if (oldRookLoc) {
  606.             (*game)->doc.theBoard[middle] = (*game)->doc.theBoard[oldRookLoc];
  607.             (*game)->doc.theBoard[oldRookLoc] = 0;
  608.         }
  609.     }        /* Castling (and therefore move) now complete. */
  610.  
  611.  
  612.     /* All that remains is some information updating for castling, king
  613.     ** position, and en-passant. */
  614.  
  615.     if (absPieceMoved == KING) {
  616.         (*game)->doc.king[color].kingLoc = moveTo;
  617.         ++(*game)->doc.king[color].kingMoves;
  618.     }
  619.  
  620.     if ((moveFrom==21) || (moveTo==21)) ++(*game)->doc.king[BLACK].rookMoves[QSIDE];
  621.     if ((moveFrom==28) || (moveTo==28)) ++(*game)->doc.king[BLACK].rookMoves[KSIDE];
  622.     if ((moveFrom==91) || (moveTo==91)) ++(*game)->doc.king[WHITE].rookMoves[QSIDE];
  623.     if ((moveFrom==98) || (moveTo==98)) ++(*game)->doc.king[WHITE].rookMoves[KSIDE];
  624.         /* This accounts for all rook moves/captures, other than castling.
  625.         ** This is necessary to keep track of rook moves/captures to determine
  626.         ** if castling is allowed.  Rook moves when castling don't have to be
  627.         ** accounted for, since the king move in the castle will prevent
  628.         ** any more castling.
  629.         */
  630.  
  631.     /* If the move was a double-pawn-push, then we have some en-passant
  632.     ** information to record.  Otherwise, we need to zero-out these values. */
  633.  
  634.     if (absPieceMoved == PAWN) {
  635.  
  636.         if ((delta != -20) && (delta != 20))
  637.             middle = moveTo = 0;
  638.                 /* If not a double-pawn-push, then record 0's for the
  639.                 ** en-passant information.  This will prevent en-passant
  640.                 ** moves from being generated. */
  641.  
  642.     }
  643.     else middle = moveTo = 0;
  644.  
  645.     (*game)->doc.enPasMove    = middle;
  646.     (*game)->doc.enPasPawnLoc = moveTo;
  647.         /* Record the en-passant information.  These values are
  648.         ** non-zero if a pawn was double-pushed. */
  649.  
  650. }
  651.  
  652.  
  653.  
  654. /*****************************************************************************/
  655.  
  656.  
  657.  
  658. #pragma segment Chess
  659. void    UnmakeMove(FileRecHndl game)
  660. {
  661.     GameListHndl    gameMoves;
  662.     short            gameIndex, numGameMoves;
  663.     short            moveFrom, moveTo, pieceCaptured, pieceCapturedFrom;
  664.     short            promoteTo, pieceMoved, color, delta, oldRookLoc, middle;
  665.  
  666.     gameIndex    = (*game)->doc.gameIndex;
  667.     numGameMoves = (*game)->doc.numGameMoves;
  668.     gameMoves    = (*game)->doc.gameMoves;
  669.  
  670.     if (!gameIndex) return;
  671.     --gameIndex;
  672.  
  673.     moveFrom          = (**gameMoves)[gameIndex].moveFrom;
  674.     moveTo            = (**gameMoves)[gameIndex].moveTo;
  675.     pieceCaptured     = (**gameMoves)[gameIndex].pieceCaptured;
  676.     pieceCapturedFrom = (**gameMoves)[gameIndex].pieceCapturedFrom;
  677.     promoteTo         = (**gameMoves)[gameIndex].promoteTo;
  678.  
  679.     if (moveFrom) {        /* The "move" could be a resign or draw.  Make sure it isn't. */
  680.         pieceMoved = (*game)->doc.theBoard[moveTo];
  681.         if (promoteTo) pieceMoved = (pieceMoved < 0) ? -1 : 1;
  682.  
  683.         (*game)->doc.theBoard[moveFrom]          = pieceMoved;
  684.         (*game)->doc.theBoard[moveTo]            = 0;
  685.         (*game)->doc.theBoard[pieceCapturedFrom] = pieceCaptured;
  686.             /* Any move now undone, except for castling.  The rook still has
  687.             ** to be put back.
  688.             */
  689.  
  690.         if ((pieceMoved == KING) || (pieceMoved == -KING)) {
  691.  
  692.             color = ((gameIndex + (*game)->doc.startColor) & 0x01);        /* Who's move it is. */
  693.             (*game)->doc.king[color].kingLoc = moveFrom;
  694.             --(*game)->doc.king[color].kingMoves;
  695.  
  696.             delta  = moveTo - moveFrom;
  697.             oldRookLoc = 0;
  698.             if (delta == -2) oldRookLoc = moveFrom - 4;
  699.             if (delta == 2)  oldRookLoc = moveFrom + 3;
  700.             if (oldRookLoc) {
  701.                 middle = (moveTo + moveFrom) / 2;
  702.                 (*game)->doc.theBoard[oldRookLoc] = (*game)->doc.theBoard[middle];
  703.                 (*game)->doc.theBoard[middle] = 0;
  704.             }
  705.         }        /* Castling now completely undone. */
  706.  
  707.         if ((moveFrom == 21) || (moveTo == 21)) --(*game)->doc.king[BLACK].rookMoves[QSIDE];
  708.         if ((moveFrom == 28) || (moveTo == 28)) --(*game)->doc.king[BLACK].rookMoves[KSIDE];
  709.         if ((moveFrom == 91) || (moveTo == 91)) --(*game)->doc.king[WHITE].rookMoves[QSIDE];
  710.         if ((moveFrom == 98) || (moveTo == 98)) --(*game)->doc.king[WHITE].rookMoves[KSIDE];
  711.             /* Undo any rook move/capture accounting.  This info is used when
  712.             ** determining of castling is allowed.
  713.             */
  714.  
  715.         (*game)->doc.enPasMove = (*game)->doc.enPasPawnLoc = 0;
  716.             /* Assume move previous to the one we just undid was not a
  717.             ** double-pawn-push.  If it was not a double-pawn-push, then
  718.             ** en-passant moves are not possible at the move number we
  719.             ** just undid.
  720.             */
  721.     }
  722.  
  723.     if (gameIndex) {        /* Restore en-passant possibilities. */
  724.  
  725.         moveFrom   = (**gameMoves)[--gameIndex].moveFrom;
  726.         moveTo     = (**gameMoves)[gameIndex++].moveTo;
  727.         pieceMoved = (*game)->doc.theBoard[moveTo];
  728.  
  729.         if ((pieceMoved == PAWN) || (pieceMoved == -PAWN)) {
  730.             delta = moveTo - moveFrom;
  731.             if ((delta == -20) || (delta == 20)) {
  732.                 (*game)->doc.enPasMove    = (moveTo + moveFrom) / 2;
  733.                 (*game)->doc.enPasPawnLoc = moveTo;
  734.             }
  735.         }
  736.     }
  737.     else {
  738.         (*game)->doc.enPasMove    = (*game)->doc.arngEnPasMove;
  739.         (*game)->doc.enPasPawnLoc = (*game)->doc.arngEnPasPawnLoc;
  740.     }
  741.  
  742.     (*game)->doc.gameIndex = gameIndex;
  743. }
  744.  
  745.  
  746.  
  747.  
  748. /*****************************************************************************/
  749.  
  750.  
  751.  
  752. #pragma segment Chess
  753. short    SquareAttacked(FileRecHndl game, short square, short color)
  754. {
  755.     short    destColor;
  756.     short    kind;
  757.     short    dirNum, dir;
  758.     short    s, dist, maxDist, dest, taker, takerLoc;
  759.  
  760.     taker    = KING + 1;        /* This is a no-take flag. */
  761.     takerLoc = 0;
  762.  
  763.     for (kind = KNIGHT; kind <= QUEEN; kind += (QUEEN - KNIGHT)) {
  764.         /* Check in the knight and queen directions. */
  765.  
  766.         for (dirNum = 0; dir = direction[kind][dirNum]; ++dirNum) {
  767.             /* The direction we will scan for an attack, for the most part. */
  768.  
  769.             maxDist = (kind == KNIGHT) ? 1 : 7;
  770.             for (s = square, dist = 1; dist <= maxDist; ++dist) {
  771.  
  772.                 s += dir;
  773.                 if ((dest = (*game)->doc.theBoard[s]) == EMPTY) continue;
  774.                     /* Empty square, so keep looking. */
  775.  
  776.                 if (dest == OBNDS) break;
  777.                     /* Can't be attacked from this direction anymore. */
  778.  
  779.                 destColor = BLACK;
  780.                 if (dest < 0) {
  781.                     destColor = WHITE;
  782.                     dest = -dest;
  783.                 }
  784.  
  785.                 if (destColor == color) break;
  786.                     /* Ran into our own piece, so no attack
  787.                     ** from this direction.
  788.                     */
  789.  
  790.                 if (dest >= taker) continue;
  791.  
  792.                 if (kind == KNIGHT)    {        /* If we are looking for knights... */
  793.                     if (dest == KNIGHT) {
  794.                         taker    = KNIGHT;
  795.                         takerLoc = s;
  796.                         dirNum   = 7;        /* Since there is 'an' attack by a knight,
  797.                                             ** we don't care if there is another.
  798.                                             ** We have already established a knight
  799.                                             ** take, so we can skip the rest of the
  800.                                             ** knight directions. */
  801.                     }
  802.                     continue;        /* Only knights can take in this direction. */
  803.                 }
  804.  
  805.                 if (dest == KING) {
  806.                     if (dist == 1) {
  807.                         taker    = KING;
  808.                         takerLoc = s;
  809.                     }
  810.                     break;
  811.                 }            /* We are looking in the non-knight move directions
  812.                             ** for attackers.  If a king is found as the possible
  813.                             ** attacker, make sure it is in range (one square away)
  814.                             ** before counting it as an attack.  If it isn't one
  815.                             ** square away, then it serves to prevent any other
  816.                             ** attack from this direction.
  817.                             */
  818.  
  819.                 if (dest == QUEEN) {
  820.                     taker    = QUEEN;
  821.                     takerLoc = s;
  822.                     break;
  823.                 }            /* If the potential attacker is a queen, then it is
  824.                             ** a valid attacker.
  825.                             */
  826.  
  827.                 if (dest == ROOK) {
  828.                     if (dirNum > 3) {
  829.                         taker    = ROOK;
  830.                         takerLoc = s;
  831.                     }
  832.                     break;
  833.                 }            /* If the potential attacker is a rook, and we are
  834.                             ** examining a rank or file, then count it as an attacker.
  835.                             ** Otherwise, the rook serves to prevent any other
  836.                             ** attack from this direction.
  837.                             */
  838.  
  839.                 if (dest == BISHOP) {
  840.                     if (dirNum < 4) {
  841.                         taker    = BISHOP;
  842.                         takerLoc = s;
  843.                     }
  844.                     break;
  845.                 }            /* If the potential attacker is a bishop, and we are
  846.                             ** examining a diagonal, then count it as an attacker.
  847.                             ** Otherwise, the bishop serves to prevent any other
  848.                             ** attack from this direction.
  849.                             */
  850.  
  851.                 if (dest == PAWN) {
  852.                     if (destColor == BLACK) {
  853.                         if ((dirNum < 2) && (dist == 1)) {
  854.                             taker    = PAWN;
  855.                             takerLoc = s;
  856.                         }
  857.                     }
  858.                     else
  859.                         if (
  860.                             (dirNum > 1) && 
  861.                             (dirNum < 4) && 
  862.                             (dist == 1)
  863.                         ) {
  864.                             taker    = PAWN;
  865.                             takerLoc = s;
  866.                         }
  867.                     break;
  868.                 }
  869.  
  870.                 break;
  871.                     /* Final case is a knight in a non-knight direction. */
  872.             }
  873.         }
  874.     }
  875.  
  876.     return(takerLoc);
  877. }
  878.  
  879.  
  880.  
  881. /*****************************************************************************/
  882.  
  883.  
  884.  
  885. #pragma segment Chess
  886. void    EndTheGame(FileRecHndl game, short endReason)
  887. {
  888.     WindowPtr    oldPort;
  889.  
  890.     MakeMove(game, 0, endReason, 0);
  891.         /* Record who resigned or draw agreement. */
  892.  
  893.     oldPort = SetFilePort(game);
  894.     ImageDocument(game, true);
  895.     AdjustGameSlider(game);
  896.     UpdateGameStatus(game);
  897.     SetPort(oldPort);
  898. }
  899.  
  900.  
  901.  
  902. /*****************************************************************************/
  903.  
  904.  
  905.  
  906. #pragma segment Chess
  907. short    WhosMove(FileRecHndl game)
  908. {
  909.     short    color;
  910.  
  911.     color  = ((*game)->doc.gameIndex ^ (*game)->doc.startColor);
  912.     return(color & 0x01);
  913. }
  914.  
  915.  
  916.  
  917. /*****************************************************************************/
  918.  
  919.  
  920.  
  921. #pragma segment Chess
  922. short    GameStatus(FileRecHndl game)
  923. {
  924.     short            i, color, kingLoc;
  925.     short            board[120], *boardPtr;
  926.     short            origGameIndex, gameIndex;
  927.     short            rep, back, pieceMoved, gameStat;
  928.     GameListHndl    gameMoves;
  929.  
  930.     gPosReps = 0;
  931.  
  932.     if ((*game)->doc.arrangeBoard) return(kGameContinues);
  933.  
  934.     for (i = 0; i < 2; ++i)
  935.         if (!(*game)->doc.timeLeft[i])
  936.             return(kYouLoseOnTime - (i ^ (*game)->doc.myColor));
  937.  
  938.     GenerateLegalMoves(game);
  939.  
  940.     gameMoves     = (*game)->doc.gameMoves;
  941.     origGameIndex = (*game)->doc.gameIndex;
  942.  
  943.     if (!(*game)->doc.numLegalMoves) {
  944.  
  945.         color = WhosMove(game);            /* Who's move it is. */
  946.  
  947.         if ((origGameIndex) && (origGameIndex == (*game)->doc.numGameMoves))
  948.             if (!(**gameMoves)[origGameIndex - 1].moveFrom)
  949.                 return((**gameMoves)[origGameIndex - 1].moveTo);
  950.  
  951.         kingLoc = (*game)->doc.king[color].kingLoc;
  952.  
  953.         if (SquareAttacked(game, kingLoc, color)) {        /* Checkmated. */
  954.             if (color == (*game)->doc.myColor) return(kYouLose);
  955.             else return(kYouWin);
  956.         }
  957.  
  958.         return(kStalemate);
  959.     }
  960.  
  961.     BlockMove((Ptr)&(*game)->doc.theBoard[0], (Ptr)&board[0], 120 * sizeof(short));
  962.  
  963.     rep = 2;            /* 2 matching current position makes 3 that match. */
  964.     back = 100;
  965.  
  966.     while ((rep) && (back) && (gameIndex = (*game)->doc.gameIndex)) {
  967.  
  968.         if ((**gameMoves)[--gameIndex].pieceCaptured) break;
  969.  
  970.         pieceMoved = (*game)->doc.theBoard[(**gameMoves)[gameIndex].moveTo];
  971.         if (pieceMoved < 0) pieceMoved = -pieceMoved;
  972.         if (pieceMoved == PAWN) break;
  973.  
  974.         UnmakeMove(game);
  975.         --back;
  976.  
  977.         boardPtr = &(*game)->doc.theBoard[0];
  978.         for (i = START_IBNDS; i < END_IBNDS; ++i) if (boardPtr[i] != board[i]) break;
  979.  
  980.         if (i == END_IBNDS) {
  981.             ++gPosReps;
  982.             if (!(--rep)) break;
  983.         }
  984.     }
  985.  
  986.     while ((*game)->doc.gameIndex != origGameIndex) MakeMove(game, 1, 0, 0);
  987.  
  988.     gameStat = kGameContinues;
  989.     if (!rep)  gameStat = kDrawByRep;
  990.     if (!back) gameStat = kDrawBy50;
  991.  
  992.     if (gameStat) (*game)->doc.numLegalMoves = 0;
  993.  
  994.     return(gameStat);
  995. }
  996.  
  997.  
  998.  
  999. /*****************************************************************************/
  1000.  
  1001.  
  1002.  
  1003. #pragma segment Chess
  1004. short    UpdateTime(FileRecHndl game, Boolean canLose)
  1005. {
  1006.     FileRecPtr    frPtr;
  1007.     short        moveColor, myColor;
  1008.     long        timeLeft, opponentTimeLeft, oldTimeLeft, diff;
  1009.  
  1010.     frPtr = *game;
  1011.     moveColor        = WhosMove(game);
  1012.     myColor          = frPtr->doc.myColor;
  1013.     timeLeft         = frPtr->doc.timeLeft[moveColor];
  1014.     opponentTimeLeft = frPtr->doc.timeLeft[moveColor ^ 1];
  1015.  
  1016.     if (!frPtr->doc.twoPlayer) myColor = moveColor;
  1017.         /* Since we are not playing over the net, both sides can lose 
  1018.         ** due to time on this machine. */
  1019.  
  1020.     if ((timeLeft > 0) && (opponentTimeLeft > 0)) {
  1021.         oldTimeLeft = timeLeft;
  1022.         diff = TickCount() - frPtr->doc.timerRefTick;
  1023.         if (diff < 0) {
  1024.             frPtr->doc.timerRefTick = TickCount();
  1025.             diff = 0;
  1026.         }
  1027.         diff /= 60;
  1028.         diff *= 60;
  1029.         if (diff >= 60) {
  1030.             if (!GameStatus(game)) {
  1031.                 timeLeft -= diff;
  1032.                 if (timeLeft < 60) timeLeft = 0;
  1033.                 if (!timeLeft)
  1034.                     if ((myColor != moveColor) || (!canLose)) timeLeft = 60;
  1035.                 frPtr->doc.timeLeft[moveColor] = timeLeft;
  1036.                 frPtr->doc.timerRefTick += diff;
  1037.                 if (!timeLeft) return(2);
  1038.                 if (timeLeft != oldTimeLeft) return(1);
  1039.             }
  1040.             else (*game)->doc.timerRefTick = TickCount();
  1041.                 /* Someone has already lost, so no time change. */
  1042.         }
  1043.     }
  1044.     else (*game)->doc.timerRefTick = TickCount();
  1045.  
  1046.     return(0);
  1047. }
  1048.  
  1049.  
  1050.  
  1051. /*****************************************************************************/
  1052.  
  1053.  
  1054.  
  1055. #pragma segment Chess
  1056. void    UpdateGameStatus(FileRecHndl game)
  1057. {
  1058.     WindowPtr        oldPort;
  1059.     ControlHandle    draw, resign;
  1060.     short            status, myColor;
  1061.     Rect            drawRect, resignRect, workRect;
  1062.     Point            endOfText;
  1063.     Boolean            hideEm, hidden;
  1064.     Str255            reasonText;
  1065.  
  1066.     if ((*game)->doc.arrangeBoard) return;
  1067.  
  1068.     draw   = (*game)->doc.draw;
  1069.     resign = (*game)->doc.resign;
  1070.  
  1071.     if (!draw) return;
  1072.  
  1073.     oldPort = SetFilePort(game);
  1074.  
  1075.     drawRect   = (*draw)->contrlRect;
  1076.     resignRect = (*resign)->contrlRect;
  1077.  
  1078.     hideEm = false;
  1079.     if (status = GameStatus(game)) hideEm = true;
  1080.  
  1081.     hidden = false;
  1082.     if (drawRect.top & 0x4000) hidden = true;
  1083.  
  1084.     workRect = drawRect;
  1085.     if (hidden) OffsetRect(&workRect, 0, -0x4000);
  1086.     workRect.right = resignRect.right;
  1087.  
  1088.     if (hideEm != hidden) {
  1089.         EraseRect(&workRect);
  1090.         MoveControl(draw,   drawRect.left,   drawRect.top ^ 0x4000);
  1091.         MoveControl(resign, resignRect.left, resignRect.top ^ 0x4000);
  1092.     }
  1093.  
  1094.     if (hideEm) {
  1095.         myColor = (*game)->doc.myColor;
  1096.         switch (status) {
  1097.             case kYouWin:
  1098.             case kYouWinOnTime:
  1099.                 status = kWhiteWins + myColor;
  1100.                 break;
  1101.             case kYouLose:
  1102.             case kYouLoseOnTime:
  1103.                 status = kBlackWins - myColor;
  1104.                 break;
  1105.         }
  1106.  
  1107.         TextMode(srcCopy);
  1108.         TextFont(systemFont);
  1109.         GetIndString(reasonText, rGameStat, status);
  1110.         MoveTo(workRect.left, workRect.top + 14);
  1111.         DrawString(reasonText);
  1112.         TextMode(srcOr);
  1113.         GetPen(&endOfText);
  1114.         workRect.left = endOfText.h;
  1115.         EraseRect(&workRect);
  1116.     }
  1117.  
  1118.     SetPort(oldPort);
  1119. }
  1120.  
  1121.  
  1122.  
  1123. /*****************************************************************************/
  1124.  
  1125.  
  1126.  
  1127. #pragma segment Chess
  1128. void    DrawButtonTitle(FileRecHndl game, short newVal)
  1129. {
  1130.     WindowPtr        oldPort;
  1131.     ControlHandle    draw;
  1132.     Rect            drawRect;
  1133.     Str255            drawButtonText;
  1134.  
  1135.     if (newVal != (*game)->doc.drawBtnState) {
  1136.  
  1137.         oldPort = SetFilePort(game);
  1138.  
  1139.         GetIndString(drawButtonText, rGameStat, kDrawButtonText + newVal);
  1140.  
  1141.         draw = (*game)->doc.draw;
  1142.         SetCTitle(draw, drawButtonText);
  1143.         (*game)->doc.drawBtnState = newVal;
  1144.  
  1145.         drawRect = (*draw)->contrlRect;
  1146.         ValidRect(&drawRect);
  1147.  
  1148.         SetPort(oldPort);
  1149.     }
  1150. }
  1151.  
  1152.  
  1153.  
  1154. /*****************************************************************************/
  1155. /*****************************************************************************/
  1156.  
  1157.  
  1158.  
  1159. #pragma segment Chess
  1160. Boolean    ComputerMove(FileRecHndl game)
  1161. {
  1162.     OSErr            err;
  1163.     FileRecHndl        workGame;
  1164.     short            numLegalMoves, moveNum, fromSq, toSq, i;
  1165.     short            wtotal, btotal, color, update;
  1166.     WindowPtr        window;
  1167.     Boolean            compMoved;
  1168.     MoveListHndl    legalMoves;
  1169.  
  1170.     compMoved = false;
  1171.  
  1172.     IncNewFileNum(false);
  1173.     err = AppDuplicateDocument(game, &workGame);
  1174.     IncNewFileNum(true);
  1175.     if (err) return(false);
  1176.  
  1177.     for (i = 0; i < 2; ++i) (*workGame)->doc.timeLeft[i] = (*game)->doc.timeLeft[i];
  1178.  
  1179.     GetPort(&window);
  1180.     (*workGame)->fileState.window = window;
  1181.  
  1182.     GenerateLegalMoves(game);
  1183.     GenerateLegalMoves(workGame);
  1184.  
  1185.     numLegalMoves = (*workGame)->doc.numLegalMoves;
  1186.     legalMoves    = (*workGame)->doc.legalMoves;
  1187.  
  1188.     if (numLegalMoves) {        /* If there is a move, pick one, any one. */
  1189.         if (numLegalMoves == 1) moveNum = 0;
  1190.         else {
  1191.             moveNum = CheckForMate(workGame, 0, 2);
  1192.             if (moveNum == -1) {
  1193.                 CalcPositionValues(workGame);
  1194.                 wtotal = gWhiteTotal >> 16;
  1195.                 btotal = gBlackTotal >> 16;
  1196.                 color  = WhosMove(workGame);
  1197.                 for (;;) {
  1198.                     if (gNumPieces == 1) {
  1199.                         if (
  1200.                             ((color == WHITE) && (wtotal == 9)) ||
  1201.                             ((color == BLACK) && (btotal == 9))
  1202.                         ) {
  1203.                             moveNum = QueenMate(workGame);
  1204.                             break;
  1205.                         }
  1206.                         if (
  1207.                             ((color == WHITE) && (wtotal == 5)) ||
  1208.                             ((color == BLACK) && (btotal == 5))
  1209.                         ) {
  1210.                             moveNum = RookMate(workGame);
  1211.                             break;
  1212.                         }
  1213.                     }
  1214.                     moveNum = BestMove(workGame);
  1215.                     break;
  1216.                 }
  1217.             }
  1218.         }
  1219.  
  1220.         if (moveNum > -1) {
  1221.             if (update = UpdateTime(game, true)) DrawTime(game);
  1222.             if (update == 2) {
  1223.                 if ((*game)->doc.twoPlayer) SendGame(game, kIsMove, nil);
  1224.                 AlertIfGameOver(game);
  1225.             }
  1226.             else {
  1227.                 fromSq = (**legalMoves)[moveNum].moveFrom;
  1228.                 toSq   = (**legalMoves)[moveNum].moveTo;
  1229.                 SlideThePiece(game, fromSq, toSq);
  1230.                 MakeMove(game, fromSq, toSq, QUEEN);
  1231.                 compMoved = true;
  1232.             }
  1233.         }
  1234.         if (moveNum == kComputerResigns) {
  1235.             EndTheGame(game, kWhiteResigns + WhosMove(game));
  1236.             if ((*game)->doc.twoPlayer) SendGame(game, kIsMove, nil);
  1237.             gComputerResigns = true;
  1238.             AlertIfGameOver(game);
  1239.         }
  1240.     }
  1241.  
  1242.     AppDisposeDocument(workGame);
  1243.     return(compMoved);
  1244. }
  1245.  
  1246.  
  1247.  
  1248. /*****************************************************************************/
  1249.  
  1250.  
  1251.  
  1252. #pragma segment Chess
  1253. short    CheckForMate(FileRecHndl game, short nodeDepth, short maxDepth)
  1254. {
  1255.     short            num, ourMove, move;
  1256.     short            color, kingLoc, result;
  1257.     MoveListHndl    node;
  1258.     EventRecord        event;
  1259.  
  1260.     ourMove = -1;
  1261.     GenerateLegalMoves(game);
  1262.  
  1263.     num  = (*game)->doc.numLegalMoves;
  1264.     node = (*game)->doc.legalMoves;
  1265.  
  1266.     (*game)->doc.legalMoves = gNodeHndl[nodeDepth];
  1267.         /* Protect the list of legal moves for this level.  Put a handle
  1268.         ** into the game where moves for the next level can be placed. */
  1269.  
  1270.     color = WhosMove(game) ^ 1;
  1271.     if (!(nodeDepth & 0x01)) {
  1272.         for (move = 0; (move < num) && (ourMove == -1); ++move) {
  1273.             MakeMove(game, (**node)[move].moveFrom, (**node)[move].moveTo, QUEEN);
  1274.             kingLoc = (*game)->doc.king[color].kingLoc;
  1275.             if (SquareAttacked(game, kingLoc, color)) {        /* The move caused check. */
  1276.                 GenerateLegalMoves(game);
  1277.                 if (!(*game)->doc.numLegalMoves) {            /* If no way out of check... */
  1278.                     ourMove = move;                            /* ...it is checkmate. */
  1279.                 }
  1280.             }
  1281.             UnmakeMove(game);
  1282.         }
  1283.         if (idleTick + 10 < TickCount()) {
  1284.             idleTick = TickCount();
  1285.             if (EventAvail(everyEvent - highLevelEventMask, &event)) ourMove = -2;
  1286.             else {
  1287.                 CTEIdle();
  1288.                 DoIdleTasks(false);
  1289.             }
  1290.         }
  1291.     }
  1292.  
  1293.     if (nodeDepth < maxDepth) {
  1294.         if (ourMove == -1) {
  1295.             for (move = 0; (move < num) && (ourMove == -1); ++move) {
  1296.                 MakeMove(game, (**node)[move].moveFrom, (**node)[move].moveTo, QUEEN);
  1297.                 result = CheckForMate(game, nodeDepth + 1, maxDepth);
  1298.                 if (result == -1) {
  1299.                     ourMove = move;
  1300.                 }
  1301.                     /* If opponent can't find saving move, then this is the move we want. */
  1302.                 if (result == -2) ourMove = -2;
  1303.                     /* User interrupted search. */
  1304.                 UnmakeMove(game);
  1305.             }
  1306.         }
  1307.     }
  1308.  
  1309.     if (!nodeDepth) {
  1310.         if (ourMove > -1) {
  1311.             MakeMove(game, (**node)[ourMove].moveFrom, (**node)[ourMove].moveTo, QUEEN);
  1312.             kingLoc = (*game)->doc.king[color].kingLoc;
  1313.             if (!SquareAttacked(game, kingLoc, color)) {    /* It is not a mate in 1. */
  1314.                 GenerateLegalMoves(game);
  1315.                 if (!(*game)->doc.numLegalMoves) ourMove = -1;
  1316.                     /* It is a stalemate.  Throw it back. */
  1317.             }
  1318.             UnmakeMove(game);
  1319.         }
  1320.     }
  1321.  
  1322.     (*game)->doc.numLegalMoves = num;
  1323.     (*game)->doc.legalMoves    = node;
  1324.  
  1325.     return(ourMove);
  1326. }
  1327.  
  1328.  
  1329.  
  1330. /*****************************************************************************/
  1331.  
  1332.  
  1333.  
  1334. #pragma segment Chess
  1335. short    BestMove(FileRecHndl game)
  1336. {
  1337.     short            bestMove, keepBestMove, num;
  1338.     short            i, color, move, from, to;
  1339.     Boolean            playOn;
  1340.     long            value, max;
  1341.     MoveListHndl    node;
  1342.     EventRecord        event;
  1343.  
  1344.     bestMove = -1;
  1345.  
  1346.     num  = (*game)->doc.numLegalMoves;
  1347.     node = (*game)->doc.legalMoves;
  1348.  
  1349.     (*game)->doc.legalMoves = gNodeHndl[kLastNode];
  1350.         /* Protect the list of legal moves for this node.  Put a different handle
  1351.         ** into the game where moves for the next level can be placed. */
  1352.  
  1353.     color  = WhosMove(game);
  1354.     playOn = false;
  1355.     if ((i = (*game)->doc.timeLeft[1 - color]) >= 0)
  1356.         if (i < 7200)
  1357.             playOn = true;
  1358.  
  1359.     for (max = 0x80000000L, move = 0; move < num; ++move) {
  1360.         from = (**node)[move].moveFrom;
  1361.         to   = (**node)[move].moveTo;
  1362.         (**node)[move].value = value = OneDeepEval(game, from, to, color);
  1363.         if (max < value) {
  1364.             max = value;
  1365.             bestMove = move;        /* Best move so far. */
  1366.         }
  1367.         if (idleTick + 10 < TickCount()) {
  1368.             idleTick = TickCount();
  1369.             if (EventAvail(everyEvent - highLevelEventMask, &event)) {
  1370.                 bestMove = -2;
  1371.                 break;
  1372.             }
  1373.             CTEIdle();
  1374.             DoIdleTasks(false);
  1375.         }
  1376.     }
  1377.  
  1378.     if (bestMove > -1) {        /* Make sure we aren't getting mated in two. */
  1379.         for (keepBestMove = bestMove;;) {
  1380.             from = (**node)[bestMove].moveFrom;
  1381.             to   = (**node)[bestMove].moveTo;
  1382.             MakeMove(game, from, to, QUEEN);
  1383.             i = CheckForMate(game, 0, 2);    /* Check for mate in 2. */
  1384.             UnmakeMove(game);
  1385.             if (i == -2) {
  1386.                 bestMove = -2;
  1387.                 break;        /* User wants to do something, so interrupt. */
  1388.             }
  1389.             if (i == -1) {
  1390.                 max = (**node)[bestMove].value;
  1391.                 if (!playOn)
  1392.                     if (max < -0x00070000)
  1393.                         if (!(Random() & 0x03))
  1394.                             bestMove = kComputerResigns;    /* Resign sometimes. */
  1395.                 break;        /* Opponent has no mate in 2 against bestMove. */
  1396.             }
  1397.             (**node)[bestMove].value = max = 0x80000000L;    /* Getting mated is bad-bad. */
  1398.             for (i = 0; i < num; ++i) {        /* Try the next best move. */
  1399.                 if (max < (**node)[i].value) {
  1400.                     max = (**node)[i].value;
  1401.                     bestMove = i;
  1402.                 }
  1403.             }
  1404.             if (max == 0x80000000L) {
  1405.                 bestMove = keepBestMove;    /* We are going to get mated.  (Bummer.) */
  1406.                 if (!playOn)
  1407.                     if (!(Random() & 0x01))
  1408.                         bestMove = kComputerResigns;    /* Resign sometimes. */
  1409.                 break;
  1410.             }
  1411.         }
  1412.     }
  1413.  
  1414.     (*game)->doc.numLegalMoves = num;
  1415.     (*game)->doc.legalMoves    = node;
  1416.  
  1417.     return(bestMove);
  1418. }
  1419.  
  1420.  
  1421.  
  1422. /*****************************************************************************/
  1423.  
  1424.  
  1425.  
  1426. #pragma segment Chess
  1427. long    OneDeepEval(FileRecHndl game, short from, short to, short color)
  1428. {
  1429.     short            material, matBalance, xcngVal, posVal;
  1430.     short            take, takerLoc, retakerLoc;
  1431.     short            val, saveBoard;
  1432.     short            *boardPtr, keepBoard[120], square, j, k, xcolor;
  1433.     short            movedPiece, piece, pieceColor, r, c, cc, delta, xcng[64], xnum, loop;
  1434.     short            dirNum, dir, s, dist, numChecks;
  1435.     long            value;
  1436.  
  1437.     MakeMove(game, from, to, QUEEN);
  1438.  
  1439.     boardPtr   = &(*game)->doc.theBoard[0];
  1440.     matBalance = xcngVal = posVal = 0;
  1441.  
  1442.     for (material = 0, square = START_IBNDS; square < END_IBNDS; ++square) {
  1443.         if (piece = boardPtr[square]) {
  1444.             if (piece != OBNDS) {
  1445.                 if (piece < 0) piece = -piece;
  1446.                 if (piece == KING)   piece = 0;
  1447.                 if (piece == QUEEN)  piece = 9;
  1448.                 if (piece == ROOK)   piece = 5;
  1449.                 if (piece == KNIGHT) piece = 3;
  1450.                 material += piece;
  1451.             }
  1452.         }
  1453.     }
  1454.  
  1455.     movedPiece = boardPtr[to];
  1456.     if (movedPiece < 0) movedPiece = -movedPiece;
  1457.  
  1458.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  1459.  
  1460.         if (piece = boardPtr[square]) {        /* Evaluate value of this piece being here. */
  1461.  
  1462.             if ((piece) && (piece != OBNDS)) {
  1463.  
  1464.                 pieceColor = BLACK;    /* Figure who's piece it is. */
  1465.                 if (piece < 0) {
  1466.                     pieceColor = WHITE;
  1467.                     piece = -piece;
  1468.                 }
  1469.  
  1470.                 j = piece;
  1471.                 if (j == QUEEN)  j = 9;
  1472.                 if (j == ROOK)   j = 5;
  1473.                 if (j == KNIGHT) j = 3;
  1474.                 if (color == pieceColor) matBalance += j;
  1475.                 else                     matBalance -= j;
  1476.  
  1477.                 r = square / 10;    /* Get row and column of piece. */
  1478.                 c = square - 10 * r - 1;
  1479.                 r -= 2;
  1480.  
  1481.                 if (pieceColor == WHITE) r = 7 - r;        /* Flip rank for white. */
  1482.  
  1483.                 if (piece == PAWN) {            /* Weight the pawn position. */
  1484.                     if (r > 4) {
  1485.                         if (r == 6) r = 63;        /* Highly advanced pawns are nice. */
  1486.                         if (r == 5) r = 30;
  1487.                     }
  1488.                     else {
  1489.                         if (material > 40) {
  1490.                             cc = c;
  1491.                             if (cc > 3) cc = 7 - cc;
  1492.                             if (r < 2) cc = 0;
  1493.                             switch (cc) {
  1494.                                 case 0:
  1495.                                     r = 0;
  1496.                                     break;
  1497.                                 case 1:
  1498.                                     if (r > 2) r = -8;
  1499.                                     else       r = 0;
  1500.                                     break;
  1501.                                 case 2:
  1502.                                     if (r == 2) r = -2;
  1503.                                     if (r == 3) {
  1504.                                         r = 6;
  1505.                                         if (c < 5) r = 11;
  1506.                                     }
  1507.                                     break;
  1508.                                 case 3:
  1509.                                     if (r == 2) r = 6;
  1510.                                     if (r == 3) {
  1511.                                         r = 6;
  1512.                                         if (c < 5) r = 11;
  1513.                                     }
  1514.                                     break;
  1515.                             }
  1516.                         }
  1517.                         if (material < 26) r *= 4;
  1518.                     }
  1519.                     for (j = 9; j <= 11; j += 2) {    /* Give weight to pawn chains. */
  1520.                         k = boardPtr[square + j];
  1521.                         if (pieceColor == WHITE) k = -k;
  1522.                         if (k == PAWN) {
  1523.                             k = boardPtr[square - j];
  1524.                             if (pieceColor == WHITE) k = -k;
  1525.                             if (k == PAWN) ++r;
  1526.                         }
  1527.                     }
  1528.                     for (j = square;;) {    /* Give negative weight for doubled pawns. */
  1529.                         j += 10;
  1530.                         if (pieceColor == WHITE) j -= 20;
  1531.                         k = boardPtr[j];
  1532.                         if (k == OBNDS) break;
  1533.                         if (pieceColor == WHITE) k = -k;
  1534.                         if (k == PAWN) r -= 4;
  1535.                     }
  1536.                     if ((c == 3) || (c == 4)) {    /* Give negative weight for backward center pawns. */
  1537.                         for (j = square - 1;;) {
  1538.                             j += 10;
  1539.                             if (pieceColor == WHITE) j -= 20;
  1540.                             k = boardPtr[j];
  1541.                             if (k == OBNDS) break;
  1542.                             if (pieceColor == WHITE) k = -k;
  1543.                             if (k == PAWN) {
  1544.                                 r -= 2;
  1545.                                 break;
  1546.                             }
  1547.                         }
  1548.                         for (j = square + 1;;) {
  1549.                             j += 10;
  1550.                             if (pieceColor == WHITE) j -= 20;
  1551.                             k = boardPtr[j];
  1552.                             if (k == OBNDS) break;
  1553.                             if (pieceColor == WHITE) k = -k;
  1554.                             if (k == PAWN) {
  1555.                                 r -= 2;
  1556.                                 break;
  1557.                             }
  1558.                         }
  1559.                     }
  1560.                     for (j = square;;) {    /* Give negative weight for doubled pawns. */
  1561.                         j += 10;
  1562.                         if (pieceColor == WHITE) j -= 20;
  1563.                         k = boardPtr[j];
  1564.                         if (k == OBNDS) break;
  1565.                         if (pieceColor == WHITE) k = -k;
  1566.                         if (k == PAWN) r -= 4;
  1567.                     }
  1568.  
  1569.                     if ((*game)->doc.gameIndex == 2)
  1570.                         if (square == 54)
  1571.                             if ((boardPtr[63] == WP) || (boardPtr[65] == WP))
  1572.                                 r = -200;
  1573.                                     /* Special-case out center-counter. */
  1574.  
  1575.                     if ((*game)->doc.gameIndex == 4)
  1576.                         if (square == 45)
  1577.                             if (boardPtr[45] == BP)
  1578.                                 if (boardPtr[54] == BP)
  1579.                                     if (boardPtr[63] == WP)
  1580.                                         if (boardPtr[64] == WP)
  1581.                                             r += (0x00010000 >> 3);
  1582.                                                 /* Give weight to declining queen's gambit. */
  1583.  
  1584.                     if ((*game)->doc.gameIndex == 4)
  1585.                         if (square == 53)
  1586.                             if (boardPtr[53] == BP)
  1587.                                 if (boardPtr[43] == BN)
  1588.                                     if (boardPtr[65] == WP)
  1589.                                         if (boardPtr[76] == WN)
  1590.                                             r += (0x00010000 >> 3);
  1591.                                                 /* Play a better e4,c5,Nf3. */
  1592.  
  1593.                     if ((*game)->doc.gameIndex == 5)
  1594.                         if (square == 54)
  1595.                             if (boardPtr[54] == WP)
  1596.                                 if (boardPtr[53] == BP)
  1597.                                     if (boardPtr[63] == WP)
  1598.                                         if (boardPtr[46] == BN)
  1599.                                             r += (0x00010000 >> 2);
  1600.                                                 /* Play a better d4,Nf6,c4,c5. */
  1601.  
  1602.                     if ((*game)->doc.gameIndex == 6)
  1603.                         if (square == 64)
  1604.                             if (boardPtr[64] == BP)
  1605.                                 if (boardPtr[43] == BN)
  1606.                                     if (boardPtr[65] == WP)
  1607.                                         if (boardPtr[76] == WN)
  1608.                                             r += (0x00010000 >> 2);
  1609.                                                 /* Play a better e4,c5,Nf3,Nc6,d4. */
  1610.  
  1611.                     if (color != pieceColor) r = -r;
  1612.                     posVal += r;
  1613.  
  1614.                 }
  1615.  
  1616.                 if (piece == KNIGHT) {        /* Give weight to centralized knights. */
  1617.                     if ((!r) || (r == 7)) {
  1618.                         r = -4;
  1619.                         if ((*game)->doc.gameIndex < 3) {
  1620.                             r = 200;
  1621.                             /* Special-case out knights early, 'cause it's really gross. */
  1622.                         }
  1623.                     }
  1624.                     else {
  1625.                         if (r == 4) r = 5;
  1626.                         if (r > 3) r = 7 - r;
  1627.                         if ((c < 2) || (c > 5))  r /= 2;
  1628.                         if ((c < 1) || (c > 6))  r /= 2;
  1629.                     }
  1630.                     if (color != pieceColor) r = -r;
  1631.                     posVal += r;
  1632.                 }
  1633.  
  1634.                 if (piece == BISHOP) {
  1635.                     j = r;
  1636.                     if (r > 5) r = 7 - r;
  1637.                         if (!r) r = -2;        /* Get those bishops developed. */
  1638.                     if ((c < 1) || (c > 6))  r /= 4;
  1639.                     if (j > 1) {        /* Give weight to knights before bishops past 2nd rank. */
  1640.                         c = (c < 4) ? 2 : 7;
  1641.                         k = boardPtr[90 + c - 70 * pieceColor];
  1642.                         if (pieceColor == WHITE) k = -k;
  1643.                         if (k == KNIGHT) r = -1;
  1644.                     }
  1645.                     if (color != pieceColor) r = -r;
  1646.                     posVal += r;
  1647.                 }
  1648.  
  1649.                 if (piece == ROOK) {    /* Give weight to rooks on open files. */
  1650.                     if (material > 25) {
  1651.                         delta = (color == BLACK) ? 10 : -10;
  1652.                         if ((c) && (c < 7)) {
  1653.                             for (j = square + delta; !boardPtr[j]; j += delta, ++r);
  1654.                             if (color != pieceColor) r = -r;
  1655.                             posVal += r;
  1656.                         }
  1657.                     }
  1658.                 }
  1659.  
  1660.                 if (piece == QUEEN) {
  1661.                     if (color == pieceColor) {
  1662.                         if (movedPiece == QUEEN) {
  1663.                             if (material > 50) {
  1664.                                 r = -2;
  1665.                                 if (color != pieceColor) r = -r;
  1666.                                 posVal += r;
  1667.                             }        /* Keep the queen from moving too much in the beginning. */
  1668.                         }
  1669.                     }
  1670.                 }
  1671.                 if (piece == KING) {    /* Give weight to no king moves other than castling. */
  1672.                     j = 0;
  1673.                     if ((*game)->doc.king[color].kingMoves < 2) {
  1674.                         j = -10;
  1675.                         if (!r) {
  1676.                             if (c == 6) j = 6;
  1677.                             if (c == 2) j = 4;
  1678.                         }
  1679.                     }
  1680.                     if (color != pieceColor) j = -j;
  1681.                     posVal += j;
  1682.                 }
  1683.  
  1684.                 if (color == pieceColor) {
  1685.  
  1686.                     for (dirNum = 0; dir = direction[piece][dirNum]; ++dirNum) {
  1687.                         for (s = square, dist = 1; dist <= distance[piece]; ++dist) {
  1688.                             if (piece == PAWN) {
  1689.                                 if (dirNum > 1) break;
  1690.                                 dir = direction[BISHOP][dirNum];
  1691.                                 if (color == BLACK) dir = -dir;
  1692.                             }
  1693.                             s += dir;
  1694.                             if ((take = (*game)->doc.theBoard[s]) == EMPTY) continue;
  1695.                             if (take == OBNDS) break;
  1696.                             xcolor = BLACK;
  1697.                             if (take < 0) {
  1698.                                 xcolor = WHITE;
  1699.                                 take = -take;
  1700.                             }
  1701.                             if (xcolor == color) break;
  1702.                             switch (j = take) {
  1703.                                 case KNIGHT:
  1704.                                     j = 3;
  1705.                                     break;
  1706.                                 case ROOK:
  1707.                                     j = 5;
  1708.                                     break;
  1709.                                 case QUEEN:
  1710.                                     j = 9;
  1711.                                     break;
  1712.                                 case KING:
  1713.                                     j = material;
  1714.                                     if (j > 39) j = 60 - j;
  1715.                                     j /= 2;
  1716.                                     break;
  1717.                             }
  1718.                             switch (k = piece) {
  1719.                                 case KNIGHT:
  1720.                                     k = 3;
  1721.                                     break;
  1722.                                 case ROOK:
  1723.                                     k = 5;
  1724.                                     break;
  1725.                                 case QUEEN:
  1726.                                     k = 9;
  1727.                                     break;
  1728.                                 case KING:
  1729.                                     k = material;
  1730.                                     if (k > 39) k = 60 - k;
  1731.                                     k /= 2;
  1732.                                     break;
  1733.                             }
  1734.                             if (j > k) posVal += (j - k) * 5;
  1735.                             else if (!SquareAttacked(game, s, color)) posVal += (j + 4);
  1736.                             break;
  1737.                         }
  1738.                     }        /* The above code calculates the aggression factor of the board.
  1739.                             ** If the attacking piece is smaller than the piece attacked, then
  1740.                             ** we add the delta value as a plus.  This factor finds forks,
  1741.                             ** chases kings, queens, etc. */
  1742.  
  1743.                             /* The below code estimates the exchange value of takes.  This
  1744.                             ** is only an estimate, as it is not an actual move analysis
  1745.                             ** of all the possible exchanges.  We are only trying to get
  1746.                             ** an estimate of the board position in this function. */
  1747.  
  1748.                     if (takerLoc = SquareAttacked(game, square, color)) {
  1749.                         saveBoard = false;
  1750.                         xnum = val = 0;
  1751.                         for (xcolor = color ^ 1; takerLoc; xcolor ^= 1) {
  1752.                             take = boardPtr[square];
  1753.                             if (take < 0) take = -take;
  1754.                             if (take == KING)   take = 512;
  1755.                             if (take == QUEEN)  take = 9;
  1756.                             if (take == ROOK)   take = 5;
  1757.                             if (take == KNIGHT) take = 3;
  1758.                             retakerLoc = SquareAttacked(game, square, xcolor);
  1759.                             if (xcolor == color) val += take;
  1760.                             else                 val -= take;
  1761.                             xcng[xnum++] = val;
  1762.                             if (retakerLoc) {
  1763.                                 if (!saveBoard) {
  1764.                                     saveBoard = true;
  1765.                                     for (j = START_IBNDS; j < END_IBNDS; ++j)
  1766.                                         keepBoard[j] = boardPtr[j];
  1767.                                 }
  1768.                                 boardPtr[square] = boardPtr[takerLoc];
  1769.                                 boardPtr[takerLoc] = 0;
  1770.                             }
  1771.                             takerLoc = retakerLoc;
  1772.                         }
  1773.                         if (saveBoard)
  1774.                             for (j = START_IBNDS; j < END_IBNDS; ++j)
  1775.                                 boardPtr[j] = keepBoard[j];
  1776.                         xcng[xnum] = xcng[xnum - 1];
  1777.                         xcolor = 1;        /* Check on opponent first. */
  1778.                         for (loop = true; loop; xcolor ^= 1) {
  1779.                             val  = xcng[xcolor];
  1780.                             k    = xnum;
  1781.                             loop = false;
  1782.                             for (j = xcolor + 2; j <= k; j += 2) {
  1783.                                 if (xcolor) {
  1784.                                     if (val > xcng[j]) {
  1785.                                         val = xcng[j];
  1786.                                         xnum = j - 1;
  1787.                                         loop = true;
  1788.                                     }
  1789.                                 }
  1790.                                 else {
  1791.                                     if (val < xcng[j]) {
  1792.                                         val = xcng[j];
  1793.                                         xnum = j - 1;
  1794.                                         loop = true;
  1795.                                     }
  1796.                                 }
  1797.                             }
  1798.                         }
  1799.                         if (xcngVal > val) xcngVal = val;
  1800.                     }
  1801.                 }
  1802.             }
  1803.         }
  1804.     }
  1805.  
  1806.     val = GameStatus(game);
  1807.     j = (100 - (*game)->doc.numLegalMoves) / 3;
  1808.     if (material > 60) j = 0;
  1809.  
  1810.     xcolor = color ^ 1;
  1811.     for (numChecks = 0;; ++numChecks) {
  1812.         if (!(SquareAttacked(game, (*game)->doc.king[xcolor].kingLoc, xcolor))) break;
  1813.         if ((*game)->doc.gameIndex < 2) break;
  1814.         UnmakeMove(game);
  1815.         UnmakeMove(game);
  1816.     }
  1817.     if (numChecks > 3) {
  1818.         j = 0;
  1819.     }
  1820.     for (; numChecks; --numChecks) {
  1821.         MakeMove(game, 1, 0, 0);
  1822.         MakeMove(game, 1, 0, 0);
  1823.     }
  1824.  
  1825.     posVal += j;
  1826.  
  1827.     if ((val >= kStalemate) && (val <= kDrawByRep)) matBalance = xcngVal = posVal = 0;
  1828.         /* Drawing may be our best move, so don't just eliminate this move. */
  1829.  
  1830.     if (gPosReps) matBalance = xcngVal = posVal = 0;
  1831.         /* Drawing may be our best move, so don't just eliminate this move. */
  1832.  
  1833.     UnmakeMove(game);
  1834.     GenerateLegalMoves(game);
  1835.  
  1836.     value = (((long)(matBalance + xcngVal)) << 16) + (((long)posVal) << 3);
  1837.     if (material > 29) value += (Random() & 0x7F);
  1838.  
  1839.     return(value);        
  1840. }
  1841.  
  1842.  
  1843.  
  1844. /*****************************************************************************/
  1845.  
  1846.  
  1847.  
  1848. #pragma segment Chess
  1849. void    SlideThePiece(FileRecHndl game, short fromSq, short toSq)
  1850. {
  1851.     short    fromRow, fromCol, toRow, toCol;
  1852.     Point    fromLoc, toLoc;
  1853.     Rect    fromRect;
  1854.  
  1855.     fromRow = (fromSq - START_IBNDS) / 10;
  1856.     fromCol = fromSq - START_IBNDS - 10 * fromRow;
  1857.     if ((*game)->doc.invertBoard) {
  1858.         fromRow = 7 - fromRow;
  1859.         fromCol = 7 - fromCol;
  1860.     }
  1861.     fromRect.top    = 1 + fromRow * kBoardSqSize;
  1862.     fromRect.left   = 1 + fromCol * kBoardSqSize;
  1863.     fromRect.bottom = fromRect.top  + 32;
  1864.     fromRect.right  = fromRect.left + 32;
  1865.  
  1866.     toRow = (toSq - START_IBNDS) / 10;
  1867.     toCol = toSq - START_IBNDS - 10 * toRow;
  1868.     if ((*game)->doc.invertBoard) {
  1869.         toRow = 7 - toRow;
  1870.         toCol = 7 - toCol;
  1871.     }
  1872.  
  1873.     fromLoc.v = fromRow * kBoardSqSize + kBoardVOffset + (kBoardSqSize / 2);
  1874.     fromLoc.h = fromCol * kBoardSqSize + kBoardHOffset + (kBoardSqSize / 2);
  1875.     toLoc.v   = toRow * kBoardSqSize + kBoardVOffset + (kBoardSqSize / 2);
  1876.     toLoc.h   = toCol * kBoardSqSize + kBoardHOffset + (kBoardSqSize / 2);
  1877.  
  1878.     MoveThePiece(game, fromSq, fromRect, fromLoc, &toLoc);
  1879. }
  1880.  
  1881.  
  1882.  
  1883. /*****************************************************************************/
  1884.  
  1885.  
  1886.  
  1887. #pragma segment Chess
  1888. void    CalcPositionValues(FileRecHndl game)
  1889. {
  1890.     short    *boardPtr, square, piece;
  1891.     long    val;
  1892.  
  1893.     boardPtr = &(*game)->doc.theBoard[0];
  1894.     gTreeValue = gWhiteTotal = gBlackTotal = gNumPieces = 0;
  1895.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  1896.         if (piece = boardPtr[square]) {
  1897.             if (piece != OBNDS) {
  1898.                 val = gTreePieceValues[piece + KING];
  1899.                 gTreeValue += val;
  1900.                 if (piece < 0) piece = -piece;
  1901.                 if (piece != KING) {
  1902.                     gPieceLoc = square;
  1903.                     ++gNumPieces;
  1904.                     if (val < 0) gWhiteTotal -= val;
  1905.                     else         gBlackTotal += val;
  1906.                 }
  1907.             }
  1908.         }
  1909.     }
  1910. }
  1911.  
  1912.  
  1913.  
  1914.